Skip to contentMethod: buildAxiomValuesFromInstance(Object, AxiomValueGatherer)
1: /**
2: * Copyright (C) 2016 Czech Technical University in Prague
3: *
4: * This program is free software: you can redistribute it and/or modify it under
5: * the terms of the GNU General Public License as published by the Free Software
6: * Foundation, either version 3 of the License, or (at your option) any
7: * later version.
8: *
9: * This program is distributed in the hope that it will be useful, but WITHOUT
10: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12: * details. You should have received a copy of the GNU General Public License
13: * along with this program. If not, see <http://www.gnu.org/licenses/>.
14: */
15: package cz.cvut.kbss.jopa.oom;
16:
17: import cz.cvut.kbss.jopa.CommonVocabulary;
18: import cz.cvut.kbss.jopa.exceptions.InvalidAssertionIdentifierException;
19: import cz.cvut.kbss.jopa.model.IRI;
20: import cz.cvut.kbss.jopa.model.descriptors.Descriptor;
21: import cz.cvut.kbss.jopa.model.metamodel.Attribute;
22: import cz.cvut.kbss.jopa.model.metamodel.EntityType;
23: import cz.cvut.kbss.jopa.model.metamodel.PropertiesSpecification;
24: import cz.cvut.kbss.jopa.utils.EntityPropertiesUtils;
25: import cz.cvut.kbss.jopa.utils.IdentifierTransformer;
26: import cz.cvut.kbss.ontodriver.model.Assertion;
27: import cz.cvut.kbss.ontodriver.model.Axiom;
28: import cz.cvut.kbss.ontodriver.model.NamedResource;
29: import cz.cvut.kbss.ontodriver.model.Value;
30:
31: import java.net.URI;
32: import java.util.*;
33: import java.util.Map.Entry;
34: import java.util.stream.Collectors;
35:
36: public class PropertiesFieldStrategy<X> extends FieldStrategy<PropertiesSpecification<? super X, ?, ?, ?>, X> {
37:
38: private final PropertiesValueHolder value = new PropertiesValueHolder();
39:
40: PropertiesFieldStrategy(EntityType<X> et, PropertiesSpecification<? super X, ?, ?, ?> att,
41: Descriptor descriptor, EntityMappingHelper mapper) {
42: super(et, att, descriptor, mapper);
43: }
44:
45: @Override
46: void addValueFromAxiom(Axiom<?> ax) {
47: if (shouldSkipAxiom(ax)) {
48: return;
49: }
50: value.addValue(ax);
51: }
52:
53: private boolean shouldSkipAxiom(Axiom<?> ax) {
54: final String property = ax.getAssertion().getIdentifier().toString();
55: // This is class assertion for entities without types
56: return property.equals(CommonVocabulary.RDF_TYPE) || isMappedAttribute(ax);
57: }
58:
59: private boolean isMappedAttribute(Axiom<?> ax) {
60: // TODO This is too simple, in case the assertion corresponds to a mapped attribute,
61: // we also have to check whether the map is suitable for the attribute. If not, it belongs to properties
62: final IRI propertyAsIri = IRI.create(ax.getAssertion().getIdentifier().toString());
63: for (Attribute<?, ?> att : et.getAttributes()) {
64: if (att.getIRI().equals(propertyAsIri)) {
65: return true;
66: }
67: }
68: return false;
69: }
70:
71: @Override
72: void buildInstanceFieldValue(Object instance) throws IllegalAccessException {
73: if (value.getValue().isEmpty()) {
74: return;
75: }
76: setValueOnInstance(instance, value.getValue());
77: }
78:
79: @Override
80: void buildAxiomValuesFromInstance(X instance, AxiomValueGatherer valueBuilder) throws IllegalAccessException {
81: final Object val = extractFieldValueFromInstance(instance);
82: final X original = mapper.getOriginalInstance(instance);
83:• if (val == null) {
84:• if (original == null) {
85: return;
86: }
87: final Map<?, Set<?>> origProps = (Map<?, Set<?>>) extractFieldValueFromInstance(original);
88:• if (origProps == null || origProps.isEmpty()) {
89: return;
90: }
91: valueBuilder.removeProperties(prepareProperties(origProps), getAttributeContext());
92: return;
93: }
94:• assert val instanceof Map;
95: final Map<?, Set<?>> props = (Map<?, Set<?>>) val;
96:• if (original == null) {
97: valueBuilder.addProperties(prepareProperties(props), getAttributeContext());
98: } else {
99: final Map<?, Set<?>> origProps = (Map<?, Set<?>>) extractFieldValueFromInstance(original);
100: final Map<Assertion, Set<Value<?>>> toRemove = resolvePropertiesToRemove(props, origProps);
101:• if (!toRemove.isEmpty()) {
102: valueBuilder.removeProperties(toRemove, getAttributeContext());
103: }
104: final Map<Assertion, Set<Value<?>>> toAdd = resolvePropertiesToAdd(props, origProps);
105:• if (!toAdd.isEmpty()) {
106: valueBuilder.addProperties(toAdd, getAttributeContext());
107: }
108: }
109: }
110:
111: private Map<Assertion, Set<Value<?>>> prepareProperties(Map<?, Set<?>> props) {
112: final Map<Assertion, Set<Value<?>>> result = new HashMap<>(props.size());
113: props.entrySet().stream().filter(e -> e.getKey() != null && e.getValue() != null)
114: .forEach(e -> result.put(propertyToAssertion(e.getKey()), objectsToValues(e.getValue())));
115: return result;
116: }
117:
118: private Assertion propertyToAssertion(Object property) {
119: try {
120: return Assertion
121: .createPropertyAssertion(EntityPropertiesUtils.getValueAsURI(property), attribute.isInferred());
122: } catch (IllegalArgumentException e) {
123: throw new InvalidAssertionIdentifierException(property + " is not a valid identifier.", e);
124: }
125: }
126:
127: private Set<Value<?>> objectsToValues(Collection<?> strValues) {
128: final Set<Value<?>> ontoValues = new HashSet<>(strValues.size());
129: ontoValues.addAll(strValues.stream().filter(v -> v != null).map(Value::new).collect(Collectors.toList()));
130: return ontoValues;
131: }
132:
133: private Map<Assertion, Set<Value<?>>> resolvePropertiesToRemove(Map<?, Set<?>> current, Map<?, Set<?>> original) {
134: return propertyDiff(original, current);
135: }
136:
137: /**
138: * The difference is counted as the properties and values which were in base but are not in the updated version.
139: */
140: private Map<Assertion, Set<Value<?>>> propertyDiff(Map<?, Set<?>> base, Map<?, Set<?>> updated) {
141: if (base == null || base.isEmpty()) {
142: return Collections.emptyMap();
143: }
144: final Map<Assertion, Set<Value<?>>> diff = new HashMap<>();
145: if (updated == null || updated.isEmpty()) {
146: diff.putAll(createAssertionsForAll(base));
147: } else {
148: for (Entry<?, Set<?>> entry : base.entrySet()) {
149: final Object key = entry.getKey();
150: if (!updated.containsKey(key) || updated.get(key) == null || updated.get(key).isEmpty()) {
151: // All values of the property are missing
152: diff.put(propertyToAssertion(key), objectsToValues(entry.getValue()));
153: } else {
154: final Set<?> currentValues = updated.get(key);
155: // Check which property values are missing
156: final List<?> removed =
157: entry.getValue().stream().filter(origVal -> !currentValues.contains(origVal))
158: .collect(Collectors.toList());
159: if (!removed.isEmpty()) {
160: diff.put(propertyToAssertion(key), objectsToValues(removed));
161: }
162: }
163: }
164: }
165: return diff;
166: }
167:
168: private Map<Assertion, Set<Value<?>>> createAssertionsForAll(Map<?, Set<?>> map) {
169: final Map<Assertion, Set<Value<?>>> diff = new HashMap<>(map.size());
170: map.forEach((key, value) -> diff.put(propertyToAssertion(key), objectsToValues(value)));
171: return diff;
172: }
173:
174: private Map<Assertion, Set<Value<?>>> resolvePropertiesToAdd(Map<?, Set<?>> current, Map<?, Set<?>> original) {
175: return propertyDiff(current, original);
176: }
177:
178: @Override
179: Assertion createAssertion() {
180: return Assertion.createUnspecifiedPropertyAssertion(attribute.isInferred());
181: }
182:
183: private class PropertiesValueHolder {
184:
185: private final Map<Object, Set<Object>> map = new HashMap<>();
186:
187: void addValue(Axiom<?> ax) {
188: final Object property = mapPropertyIdentifier(ax.getAssertion());
189: final Object value = mapPropertyValue(ax.getValue());
190: if (!map.containsKey(property)) {
191: map.put(property, new HashSet<>());
192: }
193: map.get(property).add(value);
194: }
195:
196: private Object mapPropertyIdentifier(Assertion a) {
197: final URI id = a.getIdentifier();
198: final Class<?> propertyIdType = PropertiesFieldStrategy.this.attribute.getPropertyIdentifierType();
199: assert IdentifierTransformer.isValidIdentifierType(propertyIdType);
200:
201: return IdentifierTransformer.transformToIdentifier(id, propertyIdType);
202: }
203:
204: private Object mapPropertyValue(Value<?> value) {
205: final Class<?> propertyValueType = PropertiesFieldStrategy.this.attribute.getPropertyValueType();
206: Object val = value.getValue();
207: if (val.getClass().equals(NamedResource.class)) {
208: // Value is an object property value
209: val = ((NamedResource) val).getIdentifier();
210: }
211: if (propertyValueType.isAssignableFrom(val.getClass())) {
212: return val;
213: } else {
214: // String value type was always default
215: if (propertyValueType.equals(String.class)) {
216: return val.toString();
217: } else {
218: throw new IllegalArgumentException("Cannot return value " + val + " as type " + propertyValueType);
219: }
220: }
221: }
222:
223: Map<Object, Set<Object>> getValue() {
224: return map;
225: }
226: }
227: }